package mc.support.support.pojo;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IntelliJ IDEA. <br/>
 * Author: Francis Yun    <br/>
 * Date: 2013-12-26  <br/>
 */
public abstract class ObjectPool<T> {

    private static final int DEFAULT_SIZE = 1000;
    private ReentrantLock lock;
    private BlockingQueue<T> pool;
    private int createdObjects = 0;
    private int size;
    private float initializePartRate = 0.01f;

    protected ObjectPool() {
        this( DEFAULT_SIZE );
    }

    protected ObjectPool(int size) {
        this(size, true, true );
    }

    protected ObjectPool(int size, boolean dynamicCreation, boolean initializePart) {
        init( size, dynamicCreation, initializePart );
    }

    protected void init( int size, boolean dynamicCreation, boolean initializePart ) {
        lock = new ReentrantLock();
        pool = new ArrayBlockingQueue<>(size, true);
        this.size = size;
        if ( !dynamicCreation || initializePart) {
            try {
                int limit = dynamicCreation ? (int) (size * initializePartRate) : size;
                lock.lock();
                createPool( limit );
            } finally {
                lock.unlock();
            }
        }
    }

    public T acquire() throws Exception {
        if ( pool.isEmpty() ) {
            if ( !lock.isLocked() ) {
                if ( lock.tryLock() ) {
                    try {
                        ++createdObjects;
                        return createObject();
                    } finally {
                        if (createdObjects < size) lock.unlock();
                    }
                }
            }
        }
        return pool.take();
    }

    public boolean recycle(T t) {

        boolean recycled = false;
        if ( pool.remainingCapacity() > 0 ) {
            synchronized ( pool ) {
                if ( pool.remainingCapacity() > 0 ) {
                    pool.add(t);
                    recycled = true;
                }
            }
        }

        return recycled;
    }

    private void createPool( int limit ) {
        if (lock.isLocked()) {
            for (int i = 0; i < size && i < limit; ++i) {
                T t = createObject();
                if ( null != t ) {
                    pool.add( t );
                    createdObjects++;
                }
            }
        }
    }

    protected abstract T createObject();

}
